home *** CD-ROM | disk | FTP | other *** search
/ Skunkware 98 / Skunkware 98.iso / src / mail / pine3.96.tar.gz / pine3.96.tar / pine3.96 / imap / ANSI / ipopd / ipop3d.c < prev    next >
C/C++ Source or Header  |  1997-02-25  |  16KB  |  565 lines

  1. /*
  2.  * Program:    IPOP3D - IMAP2 to POP3 conversion server
  3.  *
  4.  * Author:    Mark Crispin
  5.  *        Networks and Distributed Computing
  6.  *        Computing & Communications
  7.  *        University of Washington
  8.  *        Administration Building, AG-44
  9.  *        Seattle, WA  98195
  10.  *        Internet: MRC@CAC.Washington.EDU
  11.  *
  12.  * Date:    1 November 1990
  13.  * Last Edited:    25 February 1997
  14.  *
  15.  * Copyright 1997 by the University of Washington
  16.  *
  17.  *  Permission to use, copy, modify, and distribute this software and its
  18.  * documentation for any purpose and without fee is hereby granted, provided
  19.  * that the above copyright notice appears in all copies and that both the
  20.  * above copyright notice and this permission notice appear in supporting
  21.  * documentation, and that the name of the University of Washington not be
  22.  * used in advertising or publicity pertaining to distribution of the software
  23.  * without specific, written prior permission.  This software is made
  24.  * available "as is", and
  25.  * THE UNIVERSITY OF WASHINGTON DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED,
  26.  * WITH REGARD TO THIS SOFTWARE, INCLUDING WITHOUT LIMITATION ALL IMPLIED
  27.  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, AND IN
  28.  * NO EVENT SHALL THE UNIVERSITY OF WASHINGTON BE LIABLE FOR ANY SPECIAL,
  29.  * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  30.  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, TORT
  31.  * (INCLUDING NEGLIGENCE) OR STRICT LIABILITY, ARISING OUT OF OR IN CONNECTION
  32.  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  33.  *
  34.  */
  35.  
  36.  
  37. /* Parameter files */
  38.  
  39. #include "mail.h"
  40. #include "osdep.h"
  41. #include <stdio.h>
  42. #include <ctype.h>
  43. #include <netdb.h>
  44. #include <errno.h>
  45. extern int errno;        /* just in case */
  46. #include <signal.h>
  47. #include <pwd.h>
  48. #include "misc.h"
  49.  
  50.  
  51. /* Autologout timer */
  52. #define TIMEOUT 60*30
  53.  
  54.  
  55. /* Login tries */
  56. #define LOGTRY 3
  57.  
  58.  
  59. /* Size of temporary buffers */
  60. #define TMPLEN 1024
  61.  
  62.  
  63. /* Server states */
  64.  
  65. #define AUTHORIZATION 0
  66. #define TRANSACTION 1
  67. #define UPDATE 2
  68.  
  69. /* Eudora food */
  70.  
  71. #define STATUS "Status: %s%s\015\012\015\012"
  72. #define SLEN (sizeof (STATUS)-5)
  73.  
  74.  
  75. /* Global storage */
  76.  
  77. char *version = "3.3(20)";    /* server version */
  78. int state = AUTHORIZATION;    /* server state */
  79. MAILSTREAM *stream = NIL;    /* mailbox stream */
  80. int logtry = LOGTRY;        /* login tries */
  81. long nmsgs = 0;            /* current number of messages */
  82. long last = 0;            /* highest message accessed */
  83. long il = 0;            /* initial last message */
  84. char *host = NIL;        /* remote host name */
  85. char *user = NIL;        /* user name */
  86. char *pass = NIL;        /* password */
  87. long *msg = NIL;        /* message translation vector */
  88.  
  89.  
  90. /* Function prototypes */
  91.  
  92. void main (int argc,char *argv[]);
  93. void clkint ();
  94. void kodint ();
  95. int login (char *t,int argc,char *argv[]);
  96. long blat (char *text,long lines);
  97.  
  98. /* Main program */
  99.  
  100. void main (int argc,char *argv[])
  101. {
  102.   long i,j,k;
  103.   char *s,*t;
  104.   struct hostent *hst;
  105.   char tmp[TMPLEN];
  106. #include "linkage.c"
  107.   openlog ("ipop3d",LOG_PID,LOG_MAIL);
  108.   gethostname (tmp,TMPLEN-1);/* get local name */
  109.   s = cpystr ((hst = gethostbyname (tmp)) ? hst->h_name : tmp);
  110.   printf ("+OK %s POP3 %s w/IMAP2 client %s",s,version,
  111.       "(Comments to MRC@CAC.Washington.EDU)");
  112.   rfc822_date (tmp);        /* get date/time now */
  113.   printf (" at %s\015\012",tmp);
  114.   fflush (stdout);        /* dump output buffer */
  115.   signal (SIGALRM,clkint);    /* prepare for clock interrupt */
  116.   signal (SIGUSR2,kodint);    /* prepare for Kiss Of Death */
  117.   while (state != UPDATE) {    /* command processing loop */
  118.     alarm (TIMEOUT);        /* get a command under timeout */
  119.     while (!fgets (tmp,TMPLEN-1,stdin)) {
  120.       if (errno==EINTR) errno=0;/* ignore if some interrupt */
  121.       else {
  122.     syslog (LOG_INFO,"Connection broken while reading line from %.80s",
  123.         tcp_clienthost (tmp));
  124.     _exit (1);
  125.       }
  126.     }
  127.     alarm (0);            /* make sure timeout disabled */
  128.                 /* find end of line */
  129.     if (!strchr (tmp,'\012')) puts ("-ERR Command line too long\015");
  130.     else if (!(s = strtok (tmp," \015\012"))) puts ("-ERR Null command\015");
  131.     else {            /* dispatch based on command */
  132.       ucase (s);        /* canonicalize case */
  133.                 /* snarf argument */
  134.       t = strtok (NIL,"\015\012");
  135.                 /* QUIT command always valid */
  136.       if (!strcmp (s,"QUIT")) state = UPDATE;
  137.       else switch (state) {    /* else dispatch based on state */
  138.       case AUTHORIZATION:    /* waiting to get logged in */
  139.     if (!strcmp (s,"USER")) {
  140.       fs_give ((void **) &host);
  141.       fs_give ((void **) &user);
  142.       if (t && *t) {    /* if user name given */
  143.                 /* remote user name? */
  144.         if (s = strchr (t,':')) {
  145.           *s++ = '\0';    /* tie off host name */
  146.           host = cpystr (t);/* copy host name */
  147.           user = cpystr (s);/* copy user name */
  148.         }
  149.                 /* local user name */
  150.         else user = cpystr (t);
  151.         puts ("+OK User name accepted, password please\015");
  152.       }
  153.       else puts ("-ERR Missing username argument\015");
  154.     }
  155.     else if (user && *user && !strcmp (s,"PASS"))
  156.       state = login (t,argc,argv);
  157.                 /* (chuckle) */
  158.     else if (!strcmp (s,"RPOP")) puts ("-ERR Nice try, bunkie\015");
  159.     else puts ("-ERR Unknown command in AUTHORIZATION state\015");
  160.     break;
  161.  
  162.       case TRANSACTION:        /* logged in */
  163.     if (!strcmp (s,"STAT")) {
  164.       for (i = 1,j = 0,k = 0; i <= nmsgs; i++)
  165.         if (msg[i] > 0) {    /* message still exists? */
  166.           j++;        /* count one more undeleted message */
  167.           k += mail_elt (stream,msg[i])->rfc822_size + SLEN;
  168.         }
  169.       printf ("+OK %d %d\015\012",j,k);
  170.     }
  171.     else if (!strcmp (s,"LIST")) {
  172.       if (t && *t) {    /* argument do single message */
  173.         if (((i = atoi (t)) > 0) && (i <= nmsgs) && (msg[i] >0))
  174.           printf ("+OK %d %d\015\012",i,
  175.               mail_elt(stream,msg[i])->rfc822_size + SLEN);
  176.         else puts ("-ERR No such message\015");
  177.       }
  178.       else {        /* entire mailbox */
  179.         puts ("+OK Mailbox scan listing follows\015");
  180.         for (i = 1,j = 0,k = 0; i <= nmsgs; i++) if (msg[i] > 0)
  181.           printf ("%d %d\015\012",i,
  182.               mail_elt (stream,msg[i])->rfc822_size + SLEN);
  183.         puts (".\015");    /* end of list */
  184.       }
  185.     }
  186.     else if (!strcmp (s,"RETR")) {
  187.       if (t && *t) {    /* must have an argument */
  188.         if (((i = atoi (t)) > 0) && (i <= nmsgs) && (msg[i] > 0)) {
  189.           MESSAGECACHE *elt;
  190.                 /* update highest message accessed */
  191.           if (i > last) last = i;
  192.           printf ("+OK %d octets\015\012",
  193.               (elt = mail_elt (stream,msg[i]))->rfc822_size + SLEN);
  194.                 /* output message */
  195.           j = strlen (t = cpystr (mail_fetchheader (stream,msg[i])));
  196.           t[j - 2] = '\0';    /* flush trailing CRLF */
  197.           blat (t,-1);    /* output header */
  198.           fs_give ((void **) &t);
  199.           printf (STATUS,elt->seen ? "R" : " ",elt->recent ? " " : "O");
  200.           blat (mail_fetchtext (stream,msg[i]),-1);
  201.           puts (".\015");    /* end of list */
  202.         }
  203.         else puts ("-ERR No such message\015");
  204.       }
  205.       else puts ("-ERR Missing message number argument\015");
  206.     }
  207.     else if (!strcmp (s,"DELE")) {
  208.       if (t && *t) {    /* must have an argument */
  209.         if (((i = atoi (t)) > 0) && (i <= nmsgs) && (msg[i] > 0)) {
  210.                 /* update highest message accessed */
  211.           if (i > last) last = i;
  212.                 /* delete message */
  213.           sprintf (tmp,"%d",msg[i]);
  214.           mail_setflag (stream,tmp,"\\Deleted");
  215.           msg[i] = -msg[i];    /* note that we deleted this message */
  216.           puts ("+OK Message deleted\015");
  217.         }
  218.         else puts ("-ERR No such message\015");
  219.       }
  220.       else puts ("-ERR Missing message number argument\015");
  221.     }
  222.  
  223.     else if (!strcmp (s,"NOOP")) puts ("+OK No-op to you too!\015");
  224.     else if (!strcmp (s,"LAST")) printf ("+OK %d\015\012",last);
  225.     else if (!strcmp (s,"RSET")) {
  226.       if (nmsgs) {        /* undelete and unmark all of our messages */
  227.         for (i = 1; i <= nmsgs; i++) {
  228.                 /* ugly and inefficient, but trustworthy */
  229.           if (msg[i] < 0) {
  230.         sprintf (tmp,"%d",msg[i] = -msg[i]);
  231.         mail_clearflag (stream,tmp,i <= il ? "\\Deleted" :
  232.                   "\\Deleted \\Seen");
  233.           }
  234.           else if (i > il) {
  235.         sprintf (tmp,"%d",msg[i]);
  236.         mail_clearflag (stream,tmp,"\\Seen");
  237.           }
  238.         }
  239.         last = il;
  240.       }
  241.       puts ("+OK Reset state\015");
  242.     }
  243.     else if (!strcmp (s,"TOP")) {
  244.       if (t && *t) {    /* must have an argument */
  245.         if (((i = strtol (t,&t,10)) > 0) && (i <= nmsgs) && t && *t &&
  246.         ((j = atoi (t)) >= 0) && (msg[i] > 0)) {
  247.           MESSAGECACHE *elt = mail_elt (stream,msg[i]);
  248.                 /* update highest message accessed */
  249.           if (i > last) last = i;
  250.           puts ("+OK Top of message follows\015");
  251.                 /* output message */
  252.           k = strlen (t = cpystr (mail_fetchheader (stream,msg[i])));
  253.           t[k - 2] = '\0';    /* flush trailing CRLF */
  254.           j -= (blat (t,-1) + 2);
  255.           fs_give ((void **) &t);
  256.           printf (STATUS,elt->seen ? "R" : " ",elt->recent ? " " : "O");
  257.           if (j > 0) blat (mail_fetchtext (stream,msg[i]),j);
  258.           puts (".\015");    /* end of list */
  259.         }
  260.         else puts ("-ERR Bad argument or no such message\015");
  261.       }
  262.       else puts ("-ERR Missing message number argument\015");
  263.     }
  264.     else if (!strcmp (s,"XTND")) puts ("-ERR Sorry I can't do that\015");
  265.     else puts ("-ERR Unknown command in TRANSACTION state\015");
  266.     break;
  267.       default:
  268.         puts ("-ERR Server in unknown state\015");
  269.     break;
  270.       }
  271.     }
  272.     fflush (stdout);        /* make sure output finished */
  273.   }
  274.                 /* expunge mailbox if a stream open */
  275.   if (stream && nmsgs) mail_expunge (stream);
  276.   mail_close (stream);        /* clean up the stream */
  277.                 /* "now it's time to say sayonara..." */
  278.   if (logtry) puts ("+OK Sayonara\015");
  279.   fflush (stdout);        /* make sure output finished */
  280.   syslog (LOG_INFO,"Logout from %.80s",tcp_clienthost (tmp));
  281.   exit (0);            /* all done */
  282. }
  283.  
  284. /* Clock interrupt
  285.  */
  286.  
  287. void clkint ()
  288. {
  289.   char tmp[MAILTMPLEN];
  290.   puts ("-ERR Autologout; idle for too long\015");
  291.   syslog (LOG_INFO,"Autologout user=%.80s host=%.80s",user ? user : "???",
  292.       tcp_clienthost (tmp));
  293.   fflush (stdout);        /* make sure output blatted */
  294.   mail_close (stream);        /* try to gracefully close the stream */
  295.   stream = NIL;
  296.   exit (0);            /* die die die */
  297. }
  298.  
  299.  
  300. /* Kiss Of Death interrupt
  301.  */
  302.  
  303. void kodint ()
  304. {
  305.   char tmp[MAILTMPLEN];
  306.   puts ("-ERR Received Kiss of Death\015");
  307.   syslog (LOG_INFO,"Kiss of death user=%.80s host=%.80s",user ? user : "???",
  308.       tcp_clienthost (tmp));
  309.   fflush (stdout);        /* make sure output blatted */
  310.   mail_close (stream);        /* try to gracefully close the stream */
  311.   stream = NIL;
  312.   exit (0);            /* die die die */
  313. }
  314.  
  315. /* Parse PASS command
  316.  * Accepts: pointer to command argument
  317.  * Returns: new state
  318.  */
  319.  
  320. int login (char *t,int argc,char *argv[])
  321. {
  322.   long i,j;
  323.   char tmp[TMPLEN];
  324.   struct passwd *pwd = getpwnam ("nobody");
  325.   MESSAGECACHE *elt;
  326.   fs_give ((void **) &pass);    /* flush old passowrd */
  327.   if (!(t && *t)) {        /* if no password given */
  328.     puts ("-ERR Missing password argument\015");
  329.     return AUTHORIZATION;
  330.   }
  331.   pass = cpystr (t);        /* copy password argument */
  332.   if (host) {            /* remote; build remote INBOX */
  333.     syslog (LOG_INFO,"IMAP login to host=%.80s user=%.80s host=%.80s",host,
  334.         user,tcp_clienthost (tmp));
  335.     sprintf (tmp,"{%s}INBOX",host);
  336.     if (pwd) {            /* try to become someone harmless */
  337.       setgid (pwd->pw_gid);    /* set group ID */
  338.       setuid (pwd->pw_uid);    /* and user ID */
  339.     }
  340.   }
  341.                 /* local; attempt login, select INBOX */
  342.   else if (((i = strlen (user)) < 64) &&
  343.        server_login (user,pass,NIL,argc,argv)) {
  344.     syslog (LOG_INFO,"Login user=%.80s host=%.80s",user,tcp_clienthost (tmp));
  345.     strcpy (tmp,"INBOX");
  346.   }
  347.   else {
  348.     if (i > 128) syslog (LOG_ALERT|LOG_AUTH,"Crack attempt, host=%.80s",
  349.              tcp_clienthost (tmp));
  350.     sleep (3);            /* slow the cracker down */
  351.     if (--logtry) {        /* vague error message to confuse crackers */
  352.       puts ("-ERR Bad login\015");
  353.       syslog (LOG_INFO,"Login failure user=%.80s host=%.80s",user,
  354.           tcp_clienthost (tmp));
  355.       return AUTHORIZATION;
  356.     }
  357.     fputs ("-ERR Too many login failures\015\012",stdout);
  358.     syslog (LOG_INFO,"Excessive login failures user=%.80s host=%.80s",user,
  359.         tcp_clienthost (tmp));
  360.     return UPDATE;
  361.   }
  362.   nmsgs = 0;            /* no messages yet */
  363.   if (msg) fs_give ((void **) &msg);
  364.                 /* if mailbox non-empty */
  365.   if ((stream = mail_open (stream,tmp,NIL)) && (j = stream->nmsgs)) {
  366.     sprintf (tmp,"1:%d",j);    /* fetch fast information for all messages */
  367.     mail_fetchfast (stream,tmp);
  368.     msg = (long *) fs_get ((stream->nmsgs + 1) * sizeof (long));
  369.     for (i = 1; i <= j; i++) if (!(elt = mail_elt (stream,i))->deleted) {
  370.       msg[++nmsgs] = i;        /* note the presence of this message */
  371.       if (elt->seen) il = last = nmsgs;
  372.     }
  373.   }
  374.   printf ("+OK Mailbox open, %d messages\015\012",nmsgs);
  375.   return TRANSACTION;
  376. }
  377.  
  378. /* Blat a string with dot checking
  379.  * Accepts: string
  380.  *        maximum number of lines if greater than zero
  381.  * Returns: number of lines output
  382.  * This routine is uglier and kludgier than it should be, just to be robust
  383.  * in the case of a Tenex-format message which doesn't end in a newline.
  384.  */
  385.  
  386. long blat (char *text,long lines)
  387. {
  388.   char c = *text++;
  389.   char d = *text++;
  390.   char e;
  391.   long ret = 0;
  392.                 /* no-op if zero lines or empty string */
  393.   if (!(lines && c && d)) return;
  394.   if (c == '.') putchar ('.');    /* double string-leading dot if necessary */
  395.   while (e = *text++) {        /* copy loop */
  396.     putchar (c);        /* output character */
  397.     if (c == '\012') {        /* end of line? */
  398.       ret++;            /* count another line */
  399.       if (!--lines) return ret;    /* count down another line, return if done */
  400.                 /* double leading dot as necessary */
  401.       if (d == '.') putchar ('.');
  402.     }
  403.     c = d; d = e;        /* move to next character */
  404.   }
  405.   puts ("\015");        /* output newline instead of last 2 chars */
  406.   return ++ret;
  407. }
  408.  
  409. /* Co-routines from MAIL library */
  410.  
  411.  
  412. /* Message matches a search
  413.  * Accepts: MAIL stream
  414.  *        message number
  415.  */
  416.  
  417. void mm_searched (MAILSTREAM *stream,long msgno)
  418. {
  419.   /* Never called */
  420. }
  421.  
  422.  
  423. /* Message exists (i.e. there are that many messages in the mailbox)
  424.  * Accepts: MAIL stream
  425.  *        message number
  426.  */
  427.  
  428. void mm_exists (MAILSTREAM *stream,long number)
  429. {
  430.   /* Can't use this mechanism.  POP has no means of notifying the client of
  431.      new mail during the session. */
  432. }
  433.  
  434.  
  435. /* Message expunged
  436.  * Accepts: MAIL stream
  437.  *        message number
  438.  */
  439.  
  440. void mm_expunged (MAILSTREAM *stream,long number)
  441. {
  442.   /* This isn't used */
  443. }
  444.  
  445.  
  446. /* Message flag status change
  447.  * Accepts: MAIL stream
  448.  *        message number
  449.  */
  450.  
  451. void mm_flags (MAILSTREAM *stream,long number)
  452. {
  453.   /* This isn't used */
  454. }
  455.  
  456.  
  457. /* Mailbox found
  458.  * Accepts: Mailbox name
  459.  */
  460.  
  461. void mm_mailbox (char *string)
  462. {
  463.   /* This isn't used */
  464. }
  465.  
  466.  
  467. /* BBoard found
  468.  * Accepts: BBoard name
  469.  */
  470.  
  471. void mm_bboard (char *string)
  472. {
  473.   /* This isn't used */
  474. }
  475.  
  476. /* Notification event
  477.  * Accepts: MAIL stream
  478.  *        string to log
  479.  *        error flag
  480.  */
  481.  
  482. void mm_notify (MAILSTREAM *stream,char *string,long errflg)
  483. {
  484.   mm_log (string,errflg);    /* just do mm_log action */
  485. }
  486.  
  487.  
  488. /* Log an event for the user to see
  489.  * Accepts: string to log
  490.  *        error flag
  491.  */
  492.  
  493. void mm_log (char *string,long errflg)
  494. {
  495.   /* Not doing anything here for now */
  496. }
  497.  
  498.  
  499. /* Log an event to debugging telemetry
  500.  * Accepts: string to log
  501.  */
  502.  
  503. void mm_dlog (char *string)
  504. {
  505.   /* Not doing anything here for now */
  506. }
  507.  
  508.  
  509. /* Get user name and password for this host
  510.  * Accepts: host name
  511.  *        where to return user name
  512.  *        where to return password
  513.  *        trial count
  514.  */
  515.  
  516. void mm_login (char *host,char *username,char *password,long trial)
  517. {
  518.   strcpy (username,user);    /* set user name */
  519.   strcpy (password,pass);    /* and password */
  520. }
  521.  
  522. /* About to enter critical code
  523.  * Accepts: stream
  524.  */
  525.  
  526. void mm_critical (MAILSTREAM *stream)
  527. {
  528.   /* Not doing anything here for now */
  529. }
  530.  
  531.  
  532. /* About to exit critical code
  533.  * Accepts: stream
  534.  */
  535.  
  536. void mm_nocritical (MAILSTREAM *stream)
  537. {
  538.   /* Not doing anything here for now */
  539. }
  540.  
  541.  
  542. /* Disk error found
  543.  * Accepts: stream
  544.  *        system error code
  545.  *        flag indicating that mailbox may be clobbered
  546.  * Returns: abort flag
  547.  */
  548.  
  549. long mm_diskerror (MAILSTREAM *stream,long errcode,long serious)
  550. {
  551.   syslog (LOG_ALERT,"Retrying after disk error %.80s",strerror (errcode));
  552.   sleep (5);            /* can't do much better than this! */
  553.   return NIL;
  554. }
  555.  
  556.  
  557. /* Log a fatal error event
  558.  * Accepts: string to log
  559.  */
  560.  
  561. void mm_fatal (char *string)
  562. {
  563.   mm_log (string,ERROR);    /* shouldn't happen normally */
  564. }
  565.